﻿/**
 * ...
 * @author Krzysztof Rozalski
 */

package hapi.flash.sound;
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import haxe.Timer;

class HapiSoundPlayer {

	var sound:Sound;
	var channel:SoundChannel;
	var pos:Float;
	var volume:Float;
	var timer:Timer;
	public var isPlaying(default,null):Bool;
	
	public var position(getPosition, null):Float;
	public var duration(getDuration, null):Float;
	public var loop:Bool;
	public var playNext:HapiSoundPlayer;
	
	private static var MINIMAL_DURATION_BEFORE_SOUND_END = 250;//a little bit of black magic...
	
	public function new(sound) {
		this.pos = 0.;
		this.loop = false;
		this.sound = sound;
		this.volume = 1;
		this.isPlaying = false;
	}
	
	public function play() {
		if(!isPlaying) playNewChannel(pos);
	}
	
	public function pause() {
		stopAndSetPosition(getPosition());
	}
	
	public function stop() {
		stopAndSetPosition(0.);
	}
	
	inline function stopAndSetPosition(newPosition) {
		stopChannel();
		pos = newPosition;
		isPlaying = false;
	}
	
	public function seek( position ) {
		if (isPlaying)
			playNewChannel(position);
		else
			pos = position;
	}
	
	public function setVolume(vol:Float):Void {
		volume = applyVolumeOnChannel(vol);
	}
	
	public function getVolume() {
		return volume;
	}
	
	public function dispose():Void {
		if (channel != null) channel.stop();
		this.sound = null;
	}
	
	function playNewChannel(position:Float) {
		if ( channel != null) channel.stop();
		var limit = playNext != null ? getDuration() - MINIMAL_DURATION_BEFORE_SOUND_END : getDuration();
		if ( position < limit ) {
			var loops = loop && position == 0 ? 999999999 : 0;
			channel = sound.play(position, loops);
			volume = applyVolumeOnChannel(getVolume());
			if( channel != null) {
				this.isPlaying = true;
				this.pos = getPosition();
				if (playNext == null) {
					channel.addEventListener(Event.SOUND_COMPLETE, handleSoundCompleteEvent);
				}else {
					timer = new Timer(5);
					timer.run = checkIfSoundComplete;
				}
			}
		}else {
			handlePlaybackComplete(null);
		}
	}
	
	function handleSoundCompleteEvent(e) {
		if ( channel != null) channel.removeEventListener(Event.SOUND_COMPLETE, handleSoundCompleteEvent);
		isPlaying = false;
		if(loop) replay();
	}
	
	function checkIfSoundComplete() {
		if ( isPlaying && playNext != null && (getDuration() - getPosition() <= MINIMAL_DURATION_BEFORE_SOUND_END) ) {
			handlePlaybackComplete(null);
		}
	}
	
	function applyVolumeOnChannel(vol) {
		if (channel != null) {
			channel.soundTransform = new SoundTransform(vol);
			vol = channel.soundTransform.volume;
		}
		return vol;
	}
	
	function getPosition() {
		if (channel == null) return 0.;
		return channel.position;
	}
	
	function getDuration() {
		return sound.length;
	}
	
	function replay() {
		pos = 0.;
		play();
	}
	
	function handlePlaybackComplete( e:Event ):Void {
		if ( timer != null ) timer.stop();
		isPlaying = false;
		if (loop) {
			replay();
		}else if (playNext != null) {
			playNext.play();
		}
	}
	
	inline function stopChannel() {
		if (channel != null) channel.stop();
		if (timer != null) timer.stop();
	}
}